//****************************************************
// Porman Project 2
// Filename: CRenderDevice.cpp
// Date: 2007/10/06
// Author: KengYu Lin
// Modify:
//****************************************************
#include <string>
#include "CRenderDevice.h"
#include "CLogger.h"

namespace Porman
{

namespace GL
{

#define WORD_VBUFFER_SIZE 8192 //2048*sizeof(float)
#define WORD_IBUFFER_SIZE 4096 //2048*sizeof(unsigned short)

//--------------------------
// Utility functions
//--------------------------
std::string ws2s(const std::wstring& ws)
{
    std::string curLocale = setlocale(LC_ALL, NULL); // curLocale = "C";
    setlocale(LC_ALL, "chs");
    const wchar_t* _Source = ws.c_str();
    size_t _Dsize = 2 * ws.size() + 1;
    char *_Dest = new char[_Dsize];
    memset(_Dest,0,_Dsize);
    wcstombs(_Dest,_Source,_Dsize);
    std::string result = _Dest;
    delete []_Dest;
    setlocale(LC_ALL, curLocale.c_str());
    return result;
}

std::wstring s2ws(const std::string& s)
{
    setlocale(LC_ALL, "chs"); 
    const char* _Source = s.c_str();
    size_t _Dsize = s.size() + 1;
    wchar_t *_Dest = new wchar_t[_Dsize];
    wmemset(_Dest, 0, _Dsize);
    mbstowcs(_Dest,_Source,_Dsize);
    std::wstring result = _Dest;
    delete []_Dest;
    setlocale(LC_ALL, "C");
    return result;
}

//--------------------------
// class CRenderDevice
//--------------------------
CRenderDevice::CRenderDevice( void ) :
m_CgContext(NULL),
m_hDC(NULL),
m_hRC(NULL),
m_hThreadID(NULL),
m_bBeginThread(true),
m_iPixelFormat(0),
m_WordSys(),
m_RenderStates(),
m_RenderingInfoPerFrame()
{
    memset( m_fVBString, 0, sizeof(float)*2048 );
    memset( m_usIBString, 0, sizeof(unsigned short)*2048 );
}

CRenderDevice::~CRenderDevice( void )
{
    _uninitial();
}

bool 
CRenderDevice::switchRenderCanvas( const CRenderCanvas* pCanvas )
{
    assert(pCanvas);
    if( pCanvas )
    {
        const HDC hDC = pCanvas->m_hDC;
        if( hDC )
        {
            int iPFormat = GetPixelFormat( hDC );
            if( iPFormat == 0 || iPFormat != m_iPixelFormat )
            {
                LOG_ERROR( "Switch render context fail: hDC different pixel-format" );
                return false;
            }

            if( hDC != m_hDC )
            {
                m_hDC = hDC;
                wglMakeCurrent(NULL, NULL);
                if( wglMakeCurrent(m_hDC,m_hRC) )
                    return true;
                else
                    LOG_ERROR( "Switch render context fail: Make current fail" );
            }
            else
                return true;
        }
    }

    return false;    
}

CBufferObject* 
CRenderDevice::createBufferObject( EBufferTarget enumTarget, int iSizeInByte, EBufferUsage enumUsage, ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    CBufferObject* pBufferObj = NULL;
    try
    {
        pBufferObj = new CBufferObject( this, enumTarget, iSizeInByte, enumUsage );
        if( pBufferObj )
        {
            _addObjIntoTable( pBufferObj );
            if( pError ) (*pError) = E_SUCCESS;
            return pBufferObj;
        }
        else
        {
            assert(0);
            if( pError ) (*pError) = E_INVALID_OPERATION;
            return NULL;
        }
    }
    catch( int iErr )
    {
        if( iErr == 0 ) assert(0);
        SAFEDELETE( pBufferObj );
        if( pError ) (*pError) = E_INVALID_OPERATION;
        return NULL;
    }
}

ERESULT 
CRenderDevice::destroyBufferObject( CBufferObject** ppBufferObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    assert( (*ppBufferObj) );

    if( (*ppBufferObj) )
    {
        if( m_RenderStates.m_pCurStreamBufferObj == (*ppBufferObj) )
        {
            bindStreamSource( NULL, 0 );
        }
        else if( m_RenderStates.m_pCurIndexBufferObj == (*ppBufferObj) )
        {
            bindIndexBuffer( NULL, E_IBUFFER_TYPE_UNSIGNED_BYTE );
        }

        _deleteObjFromTable( (*ppBufferObj) );
        SAFEDELETE( (*ppBufferObj) );
        return E_SUCCESS;
    }
    else
        return E_INVALID_BUFFEROBJ;
}

ERESULT 
CRenderDevice::bindStreamSource( CBufferObject* pBufferObj, int iStride )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( pBufferObj )
    {
        assert( pBufferObj->m_eTarget == E_VERTEX_BUFFER );

        if( pBufferObj->m_eTarget == E_VERTEX_BUFFER )
        {
            if( m_RenderStates.m_pCurStreamBufferObj != pBufferObj )
                m_RenderStates.m_pCurStreamBufferObj = pBufferObj;

            if( m_RenderStates.m_iCurVertexStride != iStride )
            {
                m_RenderStates.m_iCurVertexStride = iStride;
                m_RenderStates.m_bIsVertexStrideDirty = true;
            }

            glBindBuffer( pBufferObj->m_eTarget, pBufferObj->m_uiObjID );
        }
        else
            return E_INVALID_TYPE;
    }
    else
    {
        m_RenderStates.m_pCurStreamBufferObj = NULL;
        m_RenderStates.m_bIsVertexStrideDirty = false;
        glBindBuffer( E_VERTEX_BUFFER, 0 );
    }

    return E_SUCCESS;
}

ERESULT 
CRenderDevice::bindIndexBuffer( CBufferObject* pBufferObj, EIndexBufferType enumType )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( pBufferObj )
    {
        assert( pBufferObj->m_eTarget == E_INDEX_BUFFER );

        if( pBufferObj->m_eTarget == E_INDEX_BUFFER )
        {
            if( m_RenderStates.m_pCurIndexBufferObj != pBufferObj )
                m_RenderStates.m_pCurIndexBufferObj = pBufferObj;

            if( m_RenderStates.m_eCurIndexBufferType != enumType )
                m_RenderStates.m_eCurIndexBufferType = enumType;

            glBindBuffer( E_INDEX_BUFFER, pBufferObj->m_uiObjID );
        }
        else
            return E_INVALID_TYPE;
    }
    else
    {
        m_RenderStates.m_pCurIndexBufferObj = NULL;
        m_RenderStates.m_eCurIndexBufferType = E_IBUFFER_TYPE_UNSIGNED_BYTE;
        glBindBuffer( E_INDEX_BUFFER, 0 );
    }

    return E_SUCCESS;
}

CVertexDeclObject* 
CRenderDevice::createVertexDeclObject( SVertexElement* pVertexElementsArray, ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    CVertexDeclObject* pVDeclObj = new CVertexDeclObject(this);
    if( pVDeclObj && pVertexElementsArray && pVDeclObj->_processDataArray( pVertexElementsArray ) )
    {
        _addObjIntoTable( pVDeclObj );
        if( pError ) (*pError) = E_SUCCESS;
        return pVDeclObj;
    }
    else
    {
        assert(0);
        SAFEDELETE( pVDeclObj );
        if( pError ) (*pError) = E_INVALID_OPERATION;
        return NULL;
    }
}

CVertexDeclObject*
CRenderDevice::createVertexDeclObject( CVertexElementArray* pVertexElementsArray, ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    CVertexDeclObject* pVDeclObj = new CVertexDeclObject(this);
    if( pVDeclObj && pVertexElementsArray && pVDeclObj->_processDataArray( pVertexElementsArray ) )
    {
        _addObjIntoTable( pVDeclObj );
        if( pError ) (*pError) = E_SUCCESS;
        return pVDeclObj;
    }
    else
    {
        assert(0);
        SAFEDELETE( pVDeclObj );
        if( pError ) (*pError) = E_INVALID_OPERATION;
        return NULL;
    }
}

ERESULT 
CRenderDevice::destroyVertexDeclObject( CVertexDeclObject** ppVertexDeclObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    assert( (*ppVertexDeclObj) );

    if( (*ppVertexDeclObj) )
    {
        if( m_RenderStates.m_pCurVertexDeclObj == (*ppVertexDeclObj) )
            m_RenderStates.m_pCurVertexDeclObj = NULL;

        _deleteObjFromTable( (*ppVertexDeclObj) );
        SAFEDELETE( (*ppVertexDeclObj) );
        return E_SUCCESS;
    }
    else
        return E_INVALID_BUFFEROBJ;
}

ERESULT 
CRenderDevice::bindVertexDeclObject( CVertexDeclObject* pVertexDeclObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_pCurVertexDeclObj != pVertexDeclObj )
    {
        m_RenderStates.m_pCurVertexDeclObj = pVertexDeclObj;
        m_RenderStates.m_bIsVertexDeclDirty = true;
    }

    return E_SUCCESS;
}

CShaderObject* 
CRenderDevice::createShaderObjectFromText( EShaderType enumType, const char* sText, const char* sEntryFunction, 
                                           const char** sCompileArgs, std::string* pMsg, ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    if( pError ) (*pError) = E_INVALID_OPERATION;
    if( pMsg ) (*pMsg) = "Invalid operation!!";

    CShaderObject* pShaderObj = new CShaderObject(this, enumType);
    if( pShaderObj && sText && sEntryFunction )
    {
        pShaderObj->m_sFileName = "SourceFromeText";

        CGcontext* pContextRef = _getShaderContext();
        CGprofile* pProfileRef = NULL;
        if( enumType == E_SHADERTYPE_VERTEX )
            pProfileRef = _getVsProfile();
        else 
            pProfileRef = _getPsProfile();

        pShaderObj->m_cgProgram = cgCreateProgram( (*pContextRef), CG_SOURCE, sText, (*pProfileRef), sEntryFunction, sCompileArgs );
        if( !pShaderObj->m_cgProgram )
        {
            switch( cgGetError() )
            {
            case CG_COMPILER_ERROR:
                if( pError ) (*pError) = E_COMPILE_ERROR;
                if( pMsg ) (*pMsg) = std::string( cgGetLastListing( (*pContextRef) ) );
                break;
            default:
                if( pError ) (*pError) = E_INVALID_OPERATION;
                break;
            }

            SAFEDELETE(pShaderObj);
        }
        else
        {
            if( pError ) (*pError) = E_SUCCESS;
            if( pMsg ) (*pMsg) = "";
            return pShaderObj;
        }
    }

    assert(0);
    return NULL;
}   

CShaderObject* 
CRenderDevice::createShaderObjectFromFile( EShaderType enumType, const char* sFileName, const char* sEntryFunction, 
                                           const char** sCompileArgs, std::string* pMsg, ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    if( pError ) (*pError) = E_INVALID_OPERATION;
    if( pMsg ) (*pMsg) = "Invalid operation!!";

    CShaderObject* pShaderObj = new CShaderObject(this, enumType);
    if( pShaderObj && sFileName && sEntryFunction )
    {
        pShaderObj->m_sFileName = sFileName;

        CGcontext* pContextRef = _getShaderContext();
        CGprofile* pProfileRef = NULL;
        if( enumType == E_SHADERTYPE_VERTEX )
            pProfileRef = _getVsProfile();
        else 
            pProfileRef = _getPsProfile();

        pShaderObj->m_cgProgram = cgCreateProgramFromFile( (*pContextRef), CG_SOURCE, sFileName, (*pProfileRef), sEntryFunction, sCompileArgs );
        if( !pShaderObj->m_cgProgram )
        {
            switch( cgGetError() )
            {
            case CG_FILE_READ_ERROR:
                if( pError ) (*pError) = E_FILE_READ_ERROR;
                if( pMsg ) (*pMsg) = "File read ERROR!!";
                break;
            case CG_COMPILER_ERROR:
                if( pError ) (*pError) = E_COMPILE_ERROR;
                if( pMsg ) (*pMsg) = std::string( cgGetLastListing( (*pContextRef) ) );
                break;
            default:
                if( pError ) (*pError) = E_INVALID_OPERATION;
                break;
            }

            SAFEDELETE(pShaderObj);
        }
        else
        {
            if( pError ) (*pError) = E_SUCCESS;
            if( pMsg ) (*pMsg) = "";
            return pShaderObj;
        }
    }

    assert(0);
    return NULL;
}

CShaderObject*
CRenderDevice::createShaderObjectFromObj( EShaderType enumType, const char* sObj, const char* sEntryFunction, 
                                          const char** sCompileArgs, std::string* pMsg, ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    if( pError ) (*pError) = E_INVALID_OPERATION;
    if( pMsg ) (*pMsg) = "Invalid operation!!";

    CShaderObject* pShaderObj = new CShaderObject(this, enumType);
    if( pShaderObj && sObj && sEntryFunction )
    {
        pShaderObj->m_sFileName = sObj;

        CGcontext* pContextRef = _getShaderContext();
        CGprofile* pProfileRef = NULL;
        if( enumType == E_SHADERTYPE_VERTEX )
            pProfileRef = _getVsProfile();
        else 
            pProfileRef = _getPsProfile();

        pShaderObj->m_cgProgram = cgCreateProgramFromFile( (*pContextRef), CG_OBJECT, sObj, (*pProfileRef), sEntryFunction, sCompileArgs );
        if( !pShaderObj->m_cgProgram )
        {
            switch( cgGetError() )
            {
            case CG_FILE_READ_ERROR:
                if( pError ) (*pError) = E_FILE_READ_ERROR;
                if( pMsg ) (*pMsg) = "File read ERROR!!";
                break;
            case CG_COMPILER_ERROR:
                if( pError ) (*pError) = E_COMPILE_ERROR;
                if( pMsg ) (*pMsg) = std::string( cgGetLastListing( (*pContextRef) ) );
                break;
            default:
                if( pError ) (*pError) = E_INVALID_OPERATION;
                break;
            }

            SAFEDELETE(pShaderObj);
        }
        else
        {
            if( pError ) (*pError) = E_SUCCESS;
            if( pMsg ) (*pMsg) = "";
            return pShaderObj;
        }
    }

    assert(0);
    return NULL;
}

ERESULT
CRenderDevice::destroyShaderObject( CShaderObject** ppShaderObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    assert( (*ppShaderObj) );

    if( (*ppShaderObj) )
    {
        if( m_RenderStates.m_pCurBindVertexShaderObj == (*ppShaderObj) )
        {
            bindVertexShaderObject( NULL );
        }
        //else if( m_RenderStates.m_pCurBindGeometryShaderObj == (*ppShaderObj) )
        //{
        //    bindGeometryShaderObject( NULL );
        //}
        else if( m_RenderStates.m_pCurBindPixelShaderObj == (*ppShaderObj) )
        {
            bindPixelShaderObject( NULL );
        }

        _deleteObjFromTable( (*ppShaderObj) );
        SAFEDELETE( (*ppShaderObj) );
        return E_SUCCESS;
    }
    else
        return E_INVALID_SHADEROBJ;
}

ERESULT 
CRenderDevice::compileShader( CShaderObject* pShaderObj, bool bForce, std::string* pMsg )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( !pShaderObj->isCompiled() || bForce )
    {
        ERESULT enumResult = E_INVALID_OPERATION;
        if( pShaderObj->m_cgProgram )
        {
            CGcontext* pContextRef = _getShaderContext();
            cgCompileProgram( pShaderObj->m_cgProgram );

            CGerror er = cgGetError();
            switch( er )
            {
            case CG_NO_ERROR:
                {
                    pShaderObj->_enableVaryingShaderParameters();
                    cgGLLoadProgram( pShaderObj->m_cgProgram );
                    return E_SUCCESS;
                }
                break;
            case CG_COMPILER_ERROR:
                if( pMsg ) (*pMsg) = std::string( cgGetLastListing( (*pContextRef) ) );
                enumResult = E_COMPILE_ERROR;
                break;
            default:
                enumResult = E_INVALID_OPERATION;
                break;
            }
        }

        assert(0);
        return enumResult;
    }

    return E_SUCCESS;
}

ERESULT 
CRenderDevice::bindVertexShaderObject( CShaderObject* pVertexShaderObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    unbindFXObjectPass();

    //---------------------------
    // bind shader-obj
    //---------------------------
    if( pVertexShaderObj )
    {
        if( pVertexShaderObj->isCompiled() )
        {
            cgGLEnableProfile( m_CgVertexProfile );
            if( m_RenderStates.m_pCurBindVertexShaderObj != pVertexShaderObj )
            {
                //-----------------------------------------------
                // Bind Program
                //-----------------------------------------------
                if( m_RenderStates.m_pCurBindVertexShaderObj )
                {
                    m_RenderStates.m_pCurBindVertexShaderObj->_disableVaryingShaderParameters();
                    m_RenderStates.m_pCurBindVertexShaderObj->_disableAllSamplerParameters();
                    cgGLUnbindProgram( m_CgVertexProfile );
                }

                cgGLBindProgram( pVertexShaderObj->m_cgProgram );

                //-----------------------------------------------
                // Setup shader-obj param
                //-----------------------------------------------
                m_RenderStates.m_pCurBindVertexShaderObj = pVertexShaderObj;
                m_RenderStates.m_bIsVertexShaderDirty = true;
            }
            else
                cgGLBindProgram( m_RenderStates.m_pCurBindVertexShaderObj->m_cgProgram );
        }
        else
        {
            assert(0);
            return E_NONCOMPILE_SHADEROBJ;
        }
    }
    else
    {
        //-----------------------------------------------
        // Unbind Program
        //-----------------------------------------------
        if( m_RenderStates.m_pCurBindVertexShaderObj )
        {
            m_RenderStates.m_pCurBindVertexShaderObj->_disableVaryingShaderParameters();
            m_RenderStates.m_pCurBindVertexShaderObj->_disableAllSamplerParameters();
            m_RenderStates.m_pCurBindVertexShaderObj = NULL;
            m_RenderStates.m_bIsVertexShaderDirty = false;
            cgGLUnbindProgram( m_CgVertexProfile );
        }

        cgGLDisableProfile( m_CgVertexProfile );
    }

    return E_SUCCESS;
}

//ERESULT 
//CRenderDevice::bindGeometryShaderObject( CShaderObject* pGeometryShaderObj )
//{
//    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
//    unbindFXObjectPass();
//
//    if( pGeometryShaderObj )
//    {
//        if( pGeometryShaderObj->isCompiled() )
//        {
//            cgGLEnableProfile( m_CgGeometryProfile );
//
//            if( m_RenderStates.m_pCurBindGeometryShaderObj != pGeometryShaderObj )
//            {
//                if( m_RenderStates.m_pCurBindGeometryShaderObj )
//                {
//                    m_RenderStates.m_pCurBindGeometryShaderObj->_disableAllSamplerParameters();
//                    cgGLUnbindProgram( m_CgGeometryProfile );
//                }
//
//                m_RenderStates.m_pCurBindGeometryShaderObj = pGeometryShaderObj;
//                cgGLBindProgram( pGeometryShaderObj->m_cgProgram );
//            }
//            else
//                cgGLBindProgram( m_RenderStates.m_pCurBindGeometryShaderObj->m_cgProgram );
//        }
//        else
//        {
//            assert(0);
//            return E_NONCOMPILE_SHADEROBJ;
//        }
//    }
//    else
//    {
//        if( m_RenderStates.m_pCurBindGeometryShaderObj )
//        {
//            m_RenderStates.m_pCurBindGeometryShaderObj->_disableAllSamplerParameters();
//            m_RenderStates.m_pCurBindGeometryShaderObj = NULL;
//            cgGLUnbindProgram( m_CgGeometryProfile );
//        }
//
//        cgGLDisableProfile( m_CgGeometryProfile );
//    }
//
//    return E_SUCCESS;
//}

ERESULT 
CRenderDevice::bindPixelShaderObject( CShaderObject* pPixelShaderObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    unbindFXObjectPass();

    if( pPixelShaderObj )
    {
        if( pPixelShaderObj->isCompiled() )
        {
            cgGLEnableProfile( m_CgPixelProfile );

            if( m_RenderStates.m_pCurBindPixelShaderObj != pPixelShaderObj )
            {
                if( m_RenderStates.m_pCurBindPixelShaderObj )
                {
                    m_RenderStates.m_pCurBindPixelShaderObj->_disableAllSamplerParameters();
                    cgGLUnbindProgram( m_CgPixelProfile );
                }

                m_RenderStates.m_pCurBindPixelShaderObj = pPixelShaderObj;
                cgGLBindProgram( pPixelShaderObj->m_cgProgram );
            }
            else
                cgGLBindProgram( m_RenderStates.m_pCurBindPixelShaderObj->m_cgProgram );
        }
        else
        {
            assert(0);
            return E_NONCOMPILE_SHADEROBJ;
        }
    }
    else
    {
        if( m_RenderStates.m_pCurBindPixelShaderObj )
        {
            m_RenderStates.m_pCurBindPixelShaderObj->_disableAllSamplerParameters();
            m_RenderStates.m_pCurBindPixelShaderObj = NULL;
            cgGLUnbindProgram( m_CgPixelProfile );
        }

        cgGLDisableProfile( m_CgPixelProfile );
    }

    return E_SUCCESS;
}

CFXObject* 
CRenderDevice::createFXObjectFromText( const char* sText, const char** sCompileArgs, std::string* pMsg, ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    if( pError ) (*pError) = E_INVALID_OPERATION;
    if( pMsg ) (*pMsg) = "Invalid operation!!";

    CFXObject* pFXObj = new CFXObject( this );
    if( pFXObj && sText )
    {
        CGcontext* pContextRef = _getShaderContext();
        pFXObj->m_cgEffect = cgCreateEffect( (*pContextRef), sText, sCompileArgs );
        if( !pFXObj->m_cgEffect )
        {
            switch( cgGetError() )
            {
            case CG_COMPILER_ERROR:
                if( pError ) (*pError) = E_COMPILE_ERROR;
                if( pMsg ) (*pMsg) = std::string( cgGetLastListing( (*pContextRef) ) );
                break;
            default:
                if( pError ) (*pError) = E_INVALID_OPERATION;
                break;
            }

            SAFEDELETE(pFXObj);
        }
        else
        {
            CGtechnique cgTech = cgGetFirstTechnique( pFXObj->m_cgEffect );
            while( cgTech )
            {
                CFXObject::CTechnique* pTech = new CFXObject::CTechnique( cgTech );
                pTech->m_pParentFXObj = pFXObj;
                pFXObj->m_vectTechs.push_back( pTech );
                cgTech = cgGetNextTechnique( cgTech );
            }

            if( pError ) (*pError) = E_SUCCESS;
            if( pMsg ) (*pMsg) = "";
            return pFXObj;
        }
    }

    assert(0);
    return NULL;
}

CFXObject* 
CRenderDevice::createFXObjectFromFile( const char* sFileName, const char** sCompileArgs, std::string* pMsg, ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    if( pMsg ) (*pMsg) = "Invalid operation!!";
    CFXObject* pFXObj = new CFXObject( this );

    if( pFXObj && sFileName )
    {
        CGcontext* pContextRef = _getShaderContext();
        pFXObj->m_cgEffect = cgCreateEffectFromFile( (*pContextRef), sFileName, sCompileArgs );
        if( !pFXObj->m_cgEffect )
        {
            switch( cgGetError() )
            {
            case CG_FILE_READ_ERROR:
                if( pError ) (*pError) = E_FILE_READ_ERROR;
                if( pMsg ) (*pMsg) = "File read ERROR!!";
                break;
            case CG_COMPILER_ERROR:
                if( pError ) (*pError) = E_COMPILE_ERROR;
                if( pMsg ) (*pMsg) = std::string( cgGetLastListing( (*pContextRef) ) );
                break;
            default:    
                if( pError ) (*pError) = E_INVALID_OPERATION;
                break;
            }

            SAFEDELETE(pFXObj);
        }
        else
        {
            CGtechnique cgTech = cgGetFirstTechnique( pFXObj->m_cgEffect );
            while( cgTech )
            {
                CFXObject::CTechnique* pTech = new CFXObject::CTechnique( cgTech );
                pTech->m_pParentFXObj = pFXObj;
                pFXObj->m_vectTechs.push_back( pTech );
                cgTech = cgGetNextTechnique( cgTech );
            }

            if( pError ) (*pError) = E_SUCCESS;
            if( pMsg ) (*pMsg) = "";
            return pFXObj;
        }
    }

    assert(0);
    return NULL;
}

ERESULT 
CRenderDevice::destoryFXObject( CFXObject** ppFXObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    assert( (*ppFXObj) );

    if( (*ppFXObj) )
    {
        int iCountTech = (*ppFXObj)->getNumOfTech();
        for( int i=0; i<iCountTech; i++ )
        {
            CFXObject::CTechnique* pTech = (*ppFXObj)->getIndexedTech(i);
            int iCountPass = pTech->getNumOfPass();
            for( int j=0; j<iCountPass; j++ )
            {
                CFXObject::CTechnique::CPass* pPass = pTech->getIndexedPass(j);
                if( m_RenderStates.m_pCurBindPassObj == pPass )
                    unbindFXObjectPass();
            }
        }

        _deleteObjFromTable( (*ppFXObj) );
        SAFEDELETE( (*ppFXObj) );
        return E_SUCCESS;
    }
    else
        return E_INVALID_FXOBJ;
}

ERESULT 
CRenderDevice::bindFXObjectPass( CFXObject::CTechnique::CPass* pPass, bool bRestoreState )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( pPass && pPass->m_pParentTech->isValidated() )
    {
        bindVertexShaderObject( NULL );
        //bindGeometryShaderObject( NULL );
        bindPixelShaderObject( NULL );

        SRenderStatesCallbacks::pRenderDevice = this;
        m_RenderStates.m_pCurBindPassObj = pPass;
        m_RenderStates.m_bFXResotreState = bRestoreState;
        cgSetPassState( pPass->m_cgPass );

        return E_SUCCESS;
    }
    else
    {
        assert(0);
        m_RenderStates.m_pCurBindPassObj = NULL;
        return E_INVALID_FXOBJ;
    }
}

ERESULT 
CRenderDevice::unbindFXObjectPass( void )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_pCurBindPassObj )
    {
        m_RenderStates.m_pCurBindPassObj->_disableVaryingShaderParameters();
        m_RenderStates.m_pCurBindPassObj->m_pParentTech->m_pParentFXObj->_disableAllSamplerParameters();
        if( m_RenderStates.m_bFXResotreState )
            cgResetPassState( m_RenderStates.m_pCurBindPassObj->m_cgPass );

        m_RenderStates.m_pCurBindPassObj = NULL;

        if( SRenderStatesCallbacks::pRenderDevice == this )
            SRenderStatesCallbacks::pRenderDevice = NULL;    
    }

    return E_SUCCESS;
}

CTexture2DObject* 
CRenderDevice::createTexture2DObject( CTexture2DObject::STexture2DDesc& Desc, ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    CTexture2DObject* pTextureObj = NULL;
    try
    {
        CTexture2DObject* pTextureObj = new CTexture2DObject(this, Desc);
        if( pTextureObj )
        {
            _addObjIntoTable( pTextureObj );
            if( pError ) (*pError) = E_SUCCESS;
            return pTextureObj;
        }
        else
        {
            assert(0);
            if( pError ) (*pError) = E_INVALID_OPERATION;
            return NULL;
        }
    }
    catch( int iErr )
    {
        if( iErr == 0 ) assert(0);
        SAFEDELETE( pTextureObj );
        if( pError ) (*pError) = E_INVALID_OPERATION;
        return NULL;
    }
}

CTexture3DObject* 
CRenderDevice::createTexture3DObject( CTexture3DObject::STexture3DDesc& Desc, ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    CTexture3DObject* pTextureObj = NULL;
    try
    {
        CTexture3DObject* pTextureObj = new CTexture3DObject(this, Desc);
        if( pTextureObj )
        {
            _addObjIntoTable( pTextureObj );
            if( pError ) (*pError) = E_SUCCESS;
            return pTextureObj;
        }
        else
        {
            assert(0);
            if( pError ) (*pError) = E_INVALID_OPERATION;
            return NULL;
        }
    }
    catch( int iErr )
    {
        if( iErr == 0 ) assert(0);
        SAFEDELETE( pTextureObj );
        if( pError ) (*pError) = E_INVALID_OPERATION;
        return NULL;
    }
}

CTextureCubeObject* 
CRenderDevice::createTextureCubeObject( CTextureCubeObject::STextureCubeDesc& Desc, ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    CTextureCubeObject* pTextureObj = NULL;
    try
    {
        CTextureCubeObject* pTextureObj = new CTextureCubeObject(this, Desc);
        if( pTextureObj )
        {
            _addObjIntoTable( pTextureObj );
            if( pError ) (*pError) = E_SUCCESS;
            return pTextureObj;
        }
        else
        {
            assert(0);
            if( pError ) (*pError) = E_INVALID_OPERATION;
            return NULL;
        }
    }
    catch( int iErr )
    {
        if( iErr == 0 ) assert(0);
        SAFEDELETE( pTextureObj );
        if( pError ) (*pError) = E_INVALID_OPERATION;
        return NULL;
    }
}

ERESULT 
CRenderDevice::destroyTextureObject( CTexture2DObject** ppTextureObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    assert( (*ppTextureObj) );

    if( (*ppTextureObj) )
    {
        _deleteObjFromTable( (*ppTextureObj) );
        SAFEDELETE( (*ppTextureObj) );
        return E_SUCCESS;
    }
    else
        return E_INVALID_TEXTUREOBJ;
}

ERESULT 
CRenderDevice::destroyTextureObject( CTexture3DObject** ppTextureObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    assert( (*ppTextureObj) );

    if( (*ppTextureObj) )
    {
        _deleteObjFromTable( (*ppTextureObj) );
        SAFEDELETE( (*ppTextureObj) );
        return E_SUCCESS;
    }
    else
        return E_INVALID_TEXTUREOBJ;
}

ERESULT 
CRenderDevice::destroyTextureObject( CTextureCubeObject** ppTextureObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    assert( (*ppTextureObj) );

    if( (*ppTextureObj) )
    {
        _deleteObjFromTable( (*ppTextureObj) );
        SAFEDELETE( (*ppTextureObj) );
        return E_SUCCESS;
    }
    else    
        return E_INVALID_TEXTUREOBJ;
}

CDepthStencilObject*
CRenderDevice::createDepthStencilObject( ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);
    CDepthStencilObject* pDepthStencil = NULL;
    try
    {
        CDepthStencilObject* pDepthStencil = new CDepthStencilObject(this);
        if( pDepthStencil )
        {
            _addObjIntoTable( pDepthStencil );
            if( pError ) (*pError) = E_SUCCESS;
            return pDepthStencil;
        }
        else
        {
            assert(0);
            if( pError ) (*pError) = E_INVALID_OPERATION;
            return NULL;
        }
    }
    catch( int iErr )
    {
        if( iErr == 0 ) assert(0);
        SAFEDELETE( pDepthStencil );
        if( pError ) (*pError) = E_INVALID_OPERATION;
        return NULL;
    }
}

ERESULT 
CRenderDevice::destroyDepthStencilObject( CDepthStencilObject** ppDepthStencilObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    assert( (*ppDepthStencilObj) );

    if( (*ppDepthStencilObj) )
    {
        _deleteObjFromTable( (*ppDepthStencilObj) );
        SAFEDELETE((*ppDepthStencilObj));
        return E_SUCCESS;
    }
    else
        return E_INVALID_DEPTHSTENCILOBJ;
}

CFramebufferObject* 
CRenderDevice::createFramebufferObject( ERESULT* pError )
{  
    THREAD_CHECKING_REF(m_hThreadID, pError);

    CFramebufferObject* pFramebufferObj = new CFramebufferObject(this);
    if( pFramebufferObj )
    {
        _addObjIntoTable( pFramebufferObj );
        if( pError ) (*pError) = E_SUCCESS;
        return pFramebufferObj;
    }
    else
    {
        assert(0);
        if( pError ) (*pError) = E_INVALID_OPERATION;
        return NULL;
    }
}

ERESULT 
CRenderDevice::destroyFramebufferObject( CFramebufferObject** ppFramebufferObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    assert( (*ppFramebufferObj) );

    if( (*ppFramebufferObj) )
    {
        _deleteObjFromTable( (*ppFramebufferObj) );
        SAFEDELETE((*ppFramebufferObj));
        return E_SUCCESS;
    }
    else
        return E_INVALID_FRAMEBUFFEROBJ;
}

ERESULT
CRenderDevice::bindFramebuffer( CFramebufferObject* pFramebufferObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( pFramebufferObj )
    {
        glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_RenderStates.m_uiCurFrameBufferID );

        for(int i=0; i<9; i++)
        {
            if( pFramebufferObj->m_Datas[i].m_pTex2D != NULL )
            {
                glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, 
                                           pFramebufferObj->m_Datas[i].m_iColorAttachment,
                                           GL_TEXTURE_2D, 
                                           pFramebufferObj->m_Datas[i].m_pTex2D->m_uiObjID,
                                           pFramebufferObj->m_Datas[i].m_iLevel );
            }
            else if( pFramebufferObj->m_Datas[i].m_pTex3D != NULL )
            {
                glFramebufferTexture3DEXT( GL_FRAMEBUFFER_EXT, 
                                           pFramebufferObj->m_Datas[i].m_iColorAttachment,
                                           GL_TEXTURE_3D, 
                                           pFramebufferObj->m_Datas[i].m_pTex3D->m_uiObjID,
                                           pFramebufferObj->m_Datas[i].m_iLevel,
                                           pFramebufferObj->m_Datas[i].m_iOffset);
            }
            else if( pFramebufferObj->m_Datas[i].m_pTexCube != NULL )
            {
                glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, 
                                           pFramebufferObj->m_Datas[i].m_iColorAttachment,
                                           pFramebufferObj->m_Datas[i].m_eType, 
                                           pFramebufferObj->m_Datas[i].m_pTexCube->m_uiObjID,
                                           pFramebufferObj->m_Datas[i].m_iLevel );
            }
            else if( pFramebufferObj->m_Datas[i].m_pDS != NULL )
            {
                glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, 
                                              GL_DEPTH_ATTACHMENT_EXT, 
                                              GL_RENDERBUFFER_EXT, 
                                              pFramebufferObj->m_Datas[i].m_pDS->m_uiDepthStencilObjID );

                glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, 
                                              GL_STENCIL_ATTACHMENT_EXT, 
                                              GL_RENDERBUFFER_EXT, 
                                              pFramebufferObj->m_Datas[i].m_pDS->m_uiDepthStencilObjID );
            }
        }//End for(int i=0; i<8; i++)

        unsigned int uiMessage = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
        if( uiMessage != GL_FRAMEBUFFER_COMPLETE_EXT )
        {
#ifdef _DEBUG
            switch( uiMessage ) {
            case GL_FRAMEBUFFER_COMPLETE_EXT:
                break;
            case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
                LOG_ERROR( "Framebuffer complete checking: Unsupported framebuffer format" );
                break;
            case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
                LOG_ERROR( "Framebuffer complete checking: Framebuffer incomplete attachment" );
                break;
            case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
                LOG_ERROR( "Framebuffer complete checking: Framebuffer incomplete, missing attachment" );
                break;
            case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
                LOG_ERROR( "Framebuffer complete checking: Framebuffer incomplete, attached images must have same dimensions" );
                break;
            case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
                LOG_ERROR( "Framebuffer complete checking: Framebuffer incomplete, attached images must have same format" );
                break;
            case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
                LOG_ERROR( "Framebuffer complete checking: Framebuffer incomplete, missing draw buffer" );
                break;
            case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
                LOG_ERROR( "Framebuffer complete checking: Framebuffer incomplete, missing read buffer" );
                break;
            default:
                assert(0);
            }
#endif
            glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
            return E_INVALID_FRAMEBUFFEROBJ;
        }
    }
    else
    {
        glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
    }//End if( pFramebufferObj )

    return E_SUCCESS;
}

ERESULT 
CRenderDevice::swapBuffer( bool bFlush, SRenderingInfoPerFrame* pRenderingInfoPerFrame )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( pRenderingInfoPerFrame )
    {
        (*pRenderingInfoPerFrame) = m_RenderingInfoPerFrame;
    }

    m_RenderingInfoPerFrame.reset();

    if( bFlush )
        glFlush();

    if( SwapBuffers( m_hDC ) )
    {
        return E_SUCCESS;
    }

    assert(0);
    return E_INVALID_OPERATION;
}

ERESULT
CRenderDevice::swapBuffer( CRenderCanvas* pCanvas, bool bFlush, SRenderingInfoPerFrame* pRenderingInfoPerFrame )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( pRenderingInfoPerFrame )
    {
        (*pRenderingInfoPerFrame) = m_RenderingInfoPerFrame;
    }

    m_RenderingInfoPerFrame.reset();

    if( bFlush )
        glFlush();

    if( SwapBuffers( pCanvas->m_hDC ) )
    {
        return E_SUCCESS;
    }

    assert(0);
    return E_INVALID_OPERATION;
}

ERESULT
CRenderDevice::clearBuffer( bool bClearColorbuffer, bool bClearDepthBuffer, bool bClearStencilBuffer )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    unsigned int uiFlag = 0;
    if( bClearColorbuffer )
        uiFlag |= GL_COLOR_BUFFER_BIT;

    if( bClearDepthBuffer )
        uiFlag |= GL_DEPTH_BUFFER_BIT;

    if( bClearStencilBuffer )
        uiFlag |= GL_STENCIL_BUFFER_BIT;
        
    glClear( uiFlag );
    return E_SUCCESS;
}

ERESULT 
CRenderDevice::setColorBuffer( float fRed, float fGreen, float fBlue, float fAlpha )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_fClearColor[0] != fRed   || 
        m_RenderStates.m_fClearColor[1] != fGreen || 
        m_RenderStates.m_fClearColor[2] != fBlue  || 
        m_RenderStates.m_fClearColor[3] != fAlpha )
    {
        m_RenderStates.m_fClearColor[0] = fRed;
        m_RenderStates.m_fClearColor[1] = fGreen;
        m_RenderStates.m_fClearColor[2] = fBlue;
        m_RenderStates.m_fClearColor[3] = fAlpha;

        glClearColor( fRed, fGreen, fBlue, fAlpha );
    }
    return E_SUCCESS;
}

ERESULT 
CRenderDevice::setColorBuffer( float fColor[4] )
{
    return setColorBuffer( fColor[0], fColor[1], fColor[2], fColor[3] );
}

ERESULT 
CRenderDevice::setDepthBuffer( float fDepth )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    if( m_RenderStates.m_fClearDepth != fDepth )
    {
        m_RenderStates.m_fClearDepth = fDepth;
        glClearDepth( fDepth );
    }
    return E_SUCCESS;
}

ERESULT 
CRenderDevice::setStencilBuffer( int iStencil )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    if( m_RenderStates.m_iClearStencil != iStencil )
    {
        m_RenderStates.m_iClearStencil = iStencil;
        glClearStencil( iStencil );
    }
    return E_SUCCESS;
}

COcclusionQueryObject* 
CRenderDevice::createQueryObject( ERESULT* pError )
{
    THREAD_CHECKING_REF(m_hThreadID, pError);

    COcclusionQueryObject* pQueryObj = NULL;
    try
    {
        COcclusionQueryObject* pQueryObj = new COcclusionQueryObject(this);
        if( pQueryObj )
        {
            _addObjIntoTable( pQueryObj );
            if( pError ) (*pError) = E_SUCCESS;
            return pQueryObj;
        }
        else
        {
            assert(0);
            if( pError ) (*pError) = E_INVALID_OPERATION;
            return NULL;
        }
    }
    catch( int iErr )
    {
        if( iErr == 0 ) assert(0);
        SAFEDELETE( pQueryObj );
        if( pError ) (*pError) = E_INVALID_OPERATION;
        return NULL;
    }
}

ERESULT 
CRenderDevice::destroyQueryObject( COcclusionQueryObject** ppQueryObj )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);
    assert( (*ppQueryObj) );

    if( (*ppQueryObj) )
    {
        _deleteObjFromTable( (*ppQueryObj) );
        SAFEDELETE( (*ppQueryObj) );
        return E_SUCCESS;
    }
    else
        return E_INVALID_QUERYOBJ;
}

ERESULT 
CRenderDevice::drawTexts( const char* pString, int iX, int iY, float fScale, float fColor[4] )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    //----------------------------
    // Get Geometry data:
    //----------------------------
    int iVBSizeInByte, iIBSizeInByte;
    m_WordSys.m_WordTable.parserString( pString, iVBSizeInByte, iIBSizeInByte );

    if( iVBSizeInByte > WORD_VBUFFER_SIZE )
        iVBSizeInByte = WORD_VBUFFER_SIZE;

    if( iIBSizeInByte > WORD_IBUFFER_SIZE )
        iIBSizeInByte = WORD_IBUFFER_SIZE;

    ERESULT result = E_SUCCESS;
    m_WordSys.m_WordTable.copyStringFrom( &m_fVBString[0], iVBSizeInByte, &m_usIBString[0], iIBSizeInByte );

    if( m_RenderStates.m_pWordBufferObj && 
        m_RenderStates.m_pWordIndexBufferObj )
    {
        float fOrthMatrix[16];
        float fViewProjMatrix[16];
        float fViewMatrix[16] = { fScale, 0,      0,      (float)iX,
                                  0,      fScale, 0,      (float)iY,
                                  0,      0,      fScale, 0,
                                  0,      0,      0,      1         };

        m_WordSys.setOrthoL( 0, 
                             (float)m_RenderStates.m_CurViewportData.iWidth, 0, 
                             (float)m_RenderStates.m_CurViewportData.iHeight, 0, 
                             1 , fOrthMatrix );

        m_WordSys.mulMatrix( fOrthMatrix, fViewMatrix, fViewProjMatrix );

        m_RenderStates.m_pWordBufferObj->setData( 0, iVBSizeInByte, m_fVBString );
        m_RenderStates.m_pWordIndexBufferObj->setData( 0, iIBSizeInByte, m_usIBString );

        m_WordSys.m_AlphaStateObj.m_bEnable = true;
        m_WordSys.m_AlphaStateObj.m_eCmpFunc = E_CMPFUNC_GREATER;
        m_WordSys.m_AlphaStateObj.m_fAlphaRef = 0.5f;

        bindAlphaStateObject( m_WordSys.m_AlphaStateObj );
        bindVertexShaderObject( m_WordSys.m_pVSObj );
        bindPixelShaderObject( m_WordSys.m_pPSObj );

        m_WordSys.m_pVSObj->setParameterFloatArrayRow( "g_ViewProjMatrix", 16, fViewProjMatrix );
        m_WordSys.m_pPSObj->setParameterFloatArrayRow( "g_TextColor", 4, fColor );
        m_WordSys.m_pPSObj->setSampler( "g_Sampler", &m_WordSys.m_Sampler,  m_WordSys.m_pTextObj );

        bindVertexDeclObject( m_WordSys.m_pVDeclObj );
        bindStreamSource( m_RenderStates.m_pWordBufferObj, sizeof(float)*5 );
        bindIndexBuffer( m_RenderStates.m_pWordIndexBufferObj, Porman::GL::E_IBUFFER_TYPE_UNSIGNED_SHOR );
        result = drawIndexPrimitive( Porman::GL::E_PRIMITIVE_TYPE_TRIANGLE_LIST, iVBSizeInByte/(5*4), 0, iVBSizeInByte/(5*4*4)*2 );

        m_WordSys.m_AlphaStateObj.m_bEnable = false;
        bindAlphaStateObject( m_WordSys.m_AlphaStateObj );
    }
    else
        assert(0);

    return result;
}

ERESULT 
CRenderDevice::drawPrimitive( EPrimitiveType enumType, int iStartVertex, int iPrimitiveCount )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_pCurVertexDeclObj &&
        ( m_RenderStates.m_pCurBindPassObj || m_RenderStates.m_pCurBindVertexShaderObj ) )
    {
        if( _beginDrawing() )
        {
            _drawPrimitive( enumType, iStartVertex, iPrimitiveCount );
            _endDrawing( enumType, iPrimitiveCount );
            return E_SUCCESS;
        }       
    }

    assert(0);
    return E_INVALID_OPERATION;
}

ERESULT 
CRenderDevice::drawIndexPrimitive( EPrimitiveType enumType, int iNumVertex, int iStartIndex, int iPrimitiveCount )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_pCurVertexDeclObj &&
        ( m_RenderStates.m_pCurBindPassObj || m_RenderStates.m_pCurBindVertexShaderObj ) )
    {
        if( _beginDrawing() )
        {
            _drawIndexPrimitive( enumType, iNumVertex, iStartIndex, iPrimitiveCount );
            _endDrawing( enumType, iPrimitiveCount );
            return E_SUCCESS;
        }
    }

    assert(0);
    return E_INVALID_OPERATION;
}

ERESULT 
CRenderDevice::bindScissorStateObject( const CScissorStateObject& scissor )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_ScissorStateObj != scissor )
    {   
        m_RenderStates.m_ScissorStateObj = scissor;

        if( scissor.m_bEnable )
            glEnable( GL_SCISSOR_TEST );
        else
            glDisable( GL_SCISSOR_TEST );

        glScissor( scissor.m_iX, scissor.m_iY, (unsigned int)scissor.m_iWidth, (unsigned int)scissor.m_iHeight );
    }
    return E_SUCCESS;
}

ERESULT 
CRenderDevice::bindAlphaStateObject( const CAlphaStateObject& alpha )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_AlphaStateObj != alpha )
    {
        m_RenderStates.m_AlphaStateObj = alpha;

        if( alpha.m_bEnable )
            glEnable( GL_ALPHA_TEST );
        else
            glDisable( GL_ALPHA_TEST );

        glAlphaFunc( alpha.m_eCmpFunc,  alpha.m_fAlphaRef );
    }
    return E_SUCCESS;
}

ERESULT 
CRenderDevice::bindBlendStateObject( const CBlendStateObject& blend )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_BlendStateObj != blend )
    {
        m_RenderStates.m_BlendStateObj = blend;

        if( blend.m_bEnable )
            glEnable( GL_BLEND );
        else
            glDisable( GL_BLEND );

        glBlendEquation( blend.m_eBlendOp );
        glBlendFunc( blend.m_eSrcBlendFactor, blend.m_eDstBlendFactor );
    }
    return E_SUCCESS;
}

ERESULT 
CRenderDevice::bindDepthStateObject( const CDepthStateObject& depth )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_DepthStateObj != depth )
    {
        m_RenderStates.m_DepthStateObj = depth;

        if( depth.m_bEnable )
            glEnable( GL_DEPTH_TEST );
        else
            glDisable( GL_DEPTH_TEST );

        glDepthFunc( depth.m_eCmpFunc );
    }
    return E_SUCCESS;
}


ERESULT 
CRenderDevice::bindStencilStateObject( const CStencilStateObject& stencil )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_StencilStateObj != stencil )
    {
        m_RenderStates.m_StencilStateObj = stencil;

        if( stencil.m_bEnable )
            glEnable( GL_STENCIL_TEST );
        else
            glDisable( GL_STENCIL_TEST );

        glStencilFunc( stencil.m_eCmpFunc, 
                       stencil.m_iStencilRef, 
                       stencil.m_uiStencilMask);

        glStencilOpSeparate( stencil.m_eFaceType,
                             stencil.m_eStencilFail, 
                             stencil.m_eDepthFail, 
                             stencil.m_eDepthStencilAllPass );
    }
    return E_SUCCESS;
}

ERESULT 
CRenderDevice::bindCullFaceStateObject( const CCullFaceStateObject& cullface )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_CullFaceStateObj != cullface )
    {
        m_RenderStates.m_CullFaceStateObj = cullface;

        if( cullface.m_bEnable )
            glEnable( GL_CULL_FACE );
        else
            glDisable( GL_CULL_FACE );

        glCullFace( cullface.m_eFaceType );
        glFrontFace( cullface.m_eCullFace );
    }
    return E_SUCCESS;
}

ERESULT 
CRenderDevice::bindPolygonOffsetStateObject( const CPolygonOffsetStateObject& polygonoffset )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_PolygonOffsetStateObj != polygonoffset )
    {
        m_RenderStates.m_PolygonOffsetStateObj = polygonoffset;

        if( polygonoffset.m_bEnable )
            glEnable( GL_POLYGON_OFFSET_FILL );
        else
            glDisable( GL_POLYGON_OFFSET_FILL );

        glPolygonOffset( polygonoffset.m_fFactor, polygonoffset.m_fUnits );
    }
    return E_SUCCESS;
}

ERESULT
CRenderDevice::bindPolygonModeStateObject( const CPolygonModeStateObject& polygonmode )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_PolygonModeStateObj != polygonmode )
    {
        m_RenderStates.m_PolygonModeStateObj = polygonmode;
        glPolygonMode( polygonmode.m_eFaceType, polygonmode.m_eMode );
    }
    return E_SUCCESS;
}

const CScissorStateObject& 
CRenderDevice::getScissorStateObject( void )
{
    return m_RenderStates.m_ScissorStateObj;
}

const CAlphaStateObject& 
CRenderDevice::getAlphaStateObject( void )
{
    return m_RenderStates.m_AlphaStateObj;
}

const CBlendStateObject& 
CRenderDevice::getBlendStateObject( void )
{
    return m_RenderStates.m_BlendStateObj;
}

const CDepthStateObject& 
CRenderDevice::getDepthStateObject( void )
{
    return m_RenderStates.m_DepthStateObj;
}

const CStencilStateObject& 
CRenderDevice::getStencilStateObject( void )
{
    return m_RenderStates.m_StencilStateObj;
}

const CCullFaceStateObject& 
CRenderDevice::getCullFaceStateObject( void )
{
    return m_RenderStates.m_CullFaceStateObj;
}

const CPolygonOffsetStateObject& 
CRenderDevice::getPolygonOffsetStateObject( void )
{
    return m_RenderStates.m_PolygonOffsetStateObj;
}

const CPolygonModeStateObject& 
CRenderDevice::getPolygonModeStateObject( void )
{
    return m_RenderStates.m_PolygonModeStateObj;
}

ERESULT 
CRenderDevice::setVsync( int iInterval )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_iVSyncInterval != iInterval )
    {
        m_RenderStates.m_iVSyncInterval = iInterval;
        wglSwapIntervalEXT( iInterval );
    }
    return E_SUCCESS;
}

int 
CRenderDevice::getVsync( void )
{
    return m_RenderStates.m_iVSyncInterval;
}

ERESULT
CRenderDevice::setPointSize( float fSize )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_fPointSize != fSize )
    {
        glPointSize(fSize);
        m_RenderStates.m_fPointSize = fSize;
    }
    return E_SUCCESS;
}

float 
CRenderDevice::getPointSize( void )
{
    return m_RenderStates.m_fPointSize;
}

ERESULT
CRenderDevice::setLineWidth( float fWidth )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_fLineWidth != fWidth )
    {
        m_RenderStates.m_fLineWidth = fWidth;
        glLineWidth( fWidth );
    }
    return E_SUCCESS;
}

float
CRenderDevice::getLineWidth( void )
{
    return m_RenderStates.m_fLineWidth;
}

ERESULT 
CRenderDevice::setColorBufferMask( bool bRed, bool bGreen, bool bBlue, bool bAlpha )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_bColorWrite[0] != bRed   ||
        m_RenderStates.m_bColorWrite[1] != bGreen ||
        m_RenderStates.m_bColorWrite[2] != bBlue  ||
        m_RenderStates.m_bColorWrite[3] != bAlpha )
    {
        m_RenderStates.m_bColorWrite[0] = bRed;
        m_RenderStates.m_bColorWrite[1] = bGreen;
        m_RenderStates.m_bColorWrite[2] = bBlue;
        m_RenderStates.m_bColorWrite[3] = bAlpha;

        glColorMask( bRed, bGreen, bBlue, bAlpha );
    }
    return E_SUCCESS;
}

ERESULT 
CRenderDevice::setDepthBufferMask( bool bDepth )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_bDepthWrtie != bDepth )
    {
        m_RenderStates.m_bDepthWrtie = bDepth;
        glDepthMask( bDepth );
    }
    return E_SUCCESS;
}

ERESULT 
CRenderDevice::setStencilBufferMask( EFaceType enumFace, unsigned int uiMask )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    if( m_RenderStates.m_uiStencilMask != uiMask )
    {
        m_RenderStates.m_uiStencilMask = uiMask;
        glStencilMaskSeparate( enumFace, uiMask );
    }
    return E_SUCCESS;
}

int
CRenderDevice::getNumOfShaderObject( void )
{ 
    return CBaseObject::m_iNumOfShaderObj; 
}

int 
CRenderDevice::getNumOfFXObject( void )
{
    return CBaseObject::m_iNumOfFXObj;
}

int 
CRenderDevice::getNumOfBufferObject( void )       
{ 
    return CBaseObject::m_iNumOfBufferObj; 
}

int 
CRenderDevice::getNumOfTextureObject( void )
{ 
    return CBaseObject::m_iNumOfTextureObj; 
}

int 
CRenderDevice::getNumOfFrameBufferObject( void )
{ 
    return CBaseObject::m_iNumOfFrameBufferObj; 
}

int 
CRenderDevice::getNumOfDepthStencilObject( void )
{ 
    return CBaseObject::m_iNumOfDepthStencilObj; 
}

int 
CRenderDevice::getNumOfVertexDevlObject( void )
{ 
    return CBaseObject::m_iNumOfVertexDeclObj; 
}

const unsigned char* 
CRenderDevice::getGraphicsCardVendor( void )
{
    return glGetString( GL_VENDOR );
}

ERESULT 
CRenderDevice::setViewport( int iX, int iY, int iWidth, int iHeight, float fMinZ, float fMaxZ )
{
    THREAD_CHECKING(m_hThreadID, E_THREAD_ERROR);

    m_RenderStates.m_CurViewportData.iX = iX; m_RenderStates.m_CurViewportData.iY = iY;
    m_RenderStates.m_CurViewportData.iWidth = iWidth; m_RenderStates.m_CurViewportData.iHeight = iHeight;
    m_RenderStates.m_CurViewportData.fMinZ = fMinZ; m_RenderStates.m_CurViewportData.fMaxZ = fMaxZ;

    glViewport( iX, iY, iWidth, iHeight );
    glDepthRange( fMinZ, fMaxZ );
    return E_SUCCESS;
}

DWORD CRenderDevice::getCurThreadID( void )
{
    return m_hThreadID;
}

bool CRenderDevice::beginThread( void )
{
    BOOL result = wglMakeCurrent(m_hDC, m_hRC);
    if( result )
    {
        m_bBeginThread = true;
        m_hThreadID = GetCurrentThreadId();
        return true;
    }

    assert(0);
    m_bBeginThread = false;
    return false;
}

bool CRenderDevice::endThread( void )
{
    if( m_bBeginThread )
    {
        if( wglMakeCurrent(NULL,NULL) )
        {
            m_bBeginThread = false;
            m_hThreadID = NULL;
            return true;
        }
    }
    
    assert(0);
    return false;
}

ERESULT CRenderDevice::_initial( const CRenderCanvas* pCanvas )
{
    ERESULT enumResult = E_INVALID_VALUE;
    if( pCanvas )
    {
        if( !_setSurface( pCanvas ) )
        {
            enumResult = E_INVALID_VALUE;
            goto error;
        }

        if( !_initialCg() )
        {
            enumResult = E_INITIAL_CG_FAIL;
            goto error;
        }

        m_hThreadID = GetCurrentThreadId();
        if( !m_WordSys.initial( this, "ascii_couriernew.tga" ) )
        {
            enumResult = E_INITIAL_WORDSYS_FAIL;
            goto error;
        }

        glGenFramebuffersEXT( 1, &m_RenderStates.m_uiCurFrameBufferID );
        if( m_RenderStates.m_uiCurFrameBufferID == 0 )
        {
            enumResult = E_CREATE_FRAMEBUFFER_FAIL;
            goto error;
        }

        m_RenderStates.m_pWordBufferObj = createBufferObject( E_VERTEX_BUFFER, WORD_VBUFFER_SIZE, E_DYNAMIC_DRAW, &enumResult );
        if( m_RenderStates.m_pWordBufferObj == NULL )
            goto error;

        m_RenderStates.m_pWordIndexBufferObj = createBufferObject( E_INDEX_BUFFER, WORD_IBUFFER_SIZE, E_DYNAMIC_DRAW, &enumResult );
        if( m_RenderStates.m_pWordIndexBufferObj == NULL )
            goto error;

        return E_SUCCESS;
    }

error:
    _uninitial();
    assert(0);
    return enumResult;
}

void
CRenderDevice::_uninitial( void )
{
    m_WordSys.uninitial();

    if( m_RenderStates.m_pWordBufferObj )
        destroyBufferObject( &m_RenderStates.m_pWordBufferObj );

    if( m_RenderStates.m_pWordIndexBufferObj )
        destroyBufferObject( &m_RenderStates.m_pWordIndexBufferObj );

    if( m_RenderStates.m_uiCurFrameBufferID )
    {
        bindFramebuffer(NULL);
        glDeleteFramebuffersEXT( 1, &m_RenderStates.m_uiCurFrameBufferID );
        m_RenderStates.m_uiCurFrameBufferID = 0;
    }

    if( m_listObjTable.size() != 0 )
    {
#ifdef _DEBUG
        //-----------------------------
        // Resource release checking:
        //-----------------------------
        if( CBaseObject::m_iNumOfBufferObj != 0 )
        {
            MessageBox(NULL, "Some CBufferObjects aren't released", "SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
        }

        if( CBaseObject::m_iNumOfDepthStencilObj != 0 )
        {
            MessageBox(NULL, "Some CDepthStencilObjects aren't released", "SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
        }

        if( CBaseObject::m_iNumOfFrameBufferObj != 0 )
        {
            MessageBox(NULL, "Some CFrameObjects aren't released", "SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
        }

        if( CBaseObject::m_iNumOfShaderObj != 0 )
        {
            MessageBox(NULL, "Some CShaderObjects aren't released", "SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
        }

        if( CBaseObject::m_iNumOfFXObj != 0 )
        {
            MessageBox(NULL, "Some CFXObjects aren't released", "SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
        }

        if( CBaseObject::m_iNumOfTextureObj != 0 )
        {
            MessageBox(NULL, "Some CTextureObjects aren't released", "SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
        }

        if( CBaseObject::m_iNumOfVertexDeclObj != 0 )
        {
            MessageBox(NULL, "Some CVertexDeclObjects aren't released", "SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
        }

        if( CBaseObject::m_iNumOfQueriesObj != 0 )
        {
            MessageBox(NULL, "Some COcclusionQueryObjects aren't released", "SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
        }
#endif
        //----------------------------------------------------
        // Auto delete all objs which aren't deleted by user!!
        //----------------------------------------------------
        std::list<CBaseObject*>::iterator pObjTable = m_listObjTable.begin();
        while( pObjTable != m_listObjTable.end() )
        {
#ifdef _DEBUG
            std::string sMessage = "OBJECT's NAME: ";
            //std::string sName = s2ws( (*pObjTable)->getName() );
            std::string sName = (*pObjTable)->getName();
            sMessage += sName;
            MessageBox(NULL, sMessage.c_str(), "SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
#endif
            CBaseObject* pObj = (*pObjTable);
            SAFEDELETE(pObj);
            pObjTable++;
        }

        m_listObjTable.clear();
    }//End if( m_listObjTable.size() != 0 )

    //----------------------------------------
    // Delete CG context:
    //----------------------------------------
    if( m_CgContext )
    {
        cgDestroyContext( m_CgContext );
    }   

    //---------------------------
    // Make current to null:
    //---------------------------
    if ( !wglMakeCurrent(NULL, NULL) )
        MessageBox(NULL, "Release Of DC And RC: Make current fail", "SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);

    if( m_hRC && !wglDeleteContext( m_hRC ) )
        MessageBox(NULL, "Release Of DC And RCL Delete RC fail.", "SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);

    m_hDC = NULL;
    m_hRC = NULL;
    m_iPixelFormat = 0;
}

bool
CRenderDevice::_initialCg( void )
{
    m_CgContext = cgCreateContext();
    if( m_CgContext )
    {
        //-----------------------------------
        // Set up manual compile:
        //-----------------------------------
        cgSetAutoCompile( m_CgContext, CG_COMPILE_MANUAL );

        //-----------------------------------
        // Set up Shader profile:
        //-----------------------------------
        m_CgVertexProfile = cgGLGetLatestProfile( CG_GL_VERTEX );
        if( m_CgVertexProfile == CG_PROFILE_UNKNOWN )
        {
            m_CgVertexProfile = cgGetProfile("gp4vp");
            if( m_CgVertexProfile == CG_PROFILE_UNKNOWN )
            {
                m_CgVertexProfile = cgGetProfile("arbvp1");
                if( m_CgVertexProfile == CG_PROFILE_UNKNOWN )
                    goto error;
            }
        }
        cgGLSetOptimalOptions( m_CgVertexProfile );

        //m_CgGeometryProfile = cgGLGetLatestProfile( CG_GL_GEOMETRY );
        //if( m_CgGeometryProfile == CG_PROFILE_UNKNOWN )
        //{
        //    m_CgGeometryProfile = cgGetProfile("gp4gp");
        //    if( m_CgGeometryProfile == CG_PROFILE_UNKNOWN )
        //        goto error;
        //}
        //cgGLSetOptimalOptions( m_CgGeometryProfile );

        m_CgPixelProfile = cgGLGetLatestProfile( CG_GL_FRAGMENT );
        if( m_CgPixelProfile == CG_PROFILE_UNKNOWN )
        {
            m_CgPixelProfile = cgGetProfile("gp4fp");
            if( m_CgVertexProfile == CG_PROFILE_UNKNOWN )
            {
                m_CgVertexProfile = cgGetProfile("arbfp1"); 
                if( m_CgVertexProfile == CG_PROFILE_UNKNOWN )
                    goto error;
            }
        }
        cgGLSetOptimalOptions( m_CgPixelProfile );

        //-------------------------
        // Set CgFX
        //-------------------------
        CG_ASSERT( cgGLRegisterStates( m_CgContext ) );

        CGstate vpState = CG_ASSERT( cgGetNamedState( m_CgContext, "VertexProgram" ) );
        if( cgGetStateType( vpState ) == CG_PROGRAM_TYPE )
            cgSetStateLatestProfile( vpState, m_CgVertexProfile );
        else
            goto error;

        //CGstate gpState = CG_ASSERT( cgGetNamedState( m_CgContext, "GeometryProgram" ) );
        //if( cgGetStateType( gpState ) == CG_PROGRAM_TYPE )
        //    cgSetStateLatestProfile( gpState, m_CgGeometryProfile );
        //else
        //    goto error;

        CGstate fpState = CG_ASSERT( cgGetNamedState( m_CgContext, "FragmentProgram" ) );
        if( cgGetStateType( fpState ) == CG_PROGRAM_TYPE )
            cgSetStateLatestProfile( fpState, m_CgPixelProfile );
        else
            goto error;

        CGstate state = CG_ASSERT( cgGetFirstState( m_CgContext ) );
        while( state )
        {
            std::string sStateName = CG_ASSERT( cgGetStateName( state ) );

            if( sStateName == "ClearColor" ||
                sStateName == "ClearDepth" ||
                sStateName == "ClearStencil" ||
                sStateName == "ColorMask" ||
                sStateName == "DepthMask" ||
                sStateName == "StencilMask" ||
                sStateName == "AlphaTestEnable" ||
                sStateName == "AlphaFunc" ||
                sStateName == "BlendEnable" ||
                sStateName == "BlendFunc" ||
                sStateName == "BlendEquation" || 
                sStateName == "DepthTestEnable" ||
                sStateName == "DepthFunc" ||
                sStateName == "StencilTestEnable" ||
                sStateName == "StencilFunc" ||
                sStateName == "StencilOp" ||
                sStateName == "ScissorTestEnable" ||
                sStateName == "Scissor" ||
                sStateName == "CullFaceEnable" ||
                sStateName == "CullFace" ||
                sStateName == "FrontFace" ||
                sStateName == "PolygonOffsetFillEnable" ||
                sStateName == "PolygonOffset" )   
            {
                CG_ASSERT( cgSetStateCallbacks( state, SRenderStatesCallbacks::set, 
                                                       SRenderStatesCallbacks::reset,
                                                       NULL ) );
            }

            state = CG_ASSERT( cgGetNextState( state ) );
        }//End while( state )

        return true;
    }
error:
    assert(0);
    return false;
}

void 
CRenderDevice::_addObjIntoTable( CBaseObject* pObj )
{
    m_listObjTable.push_back( pObj );
}

void 
CRenderDevice::_deleteObjFromTable( const CBaseObject* pObj )
{
    std::list<CBaseObject*>::iterator pList = m_listObjTable.begin();
    while( pList != m_listObjTable.end() )
    {
        if( *pList == pObj )
        {
            m_listObjTable.erase( pList );
            break;
        }
        pList++;
    }
}

bool 
CRenderDevice::_setSurface( const CRenderCanvas* pCanvas )
{
    //m_hRC = wglCreateContextAttribsARB( pCanvas->m_hDC, 0, NULL );
    m_hRC = wglCreateContext( pCanvas->m_hDC );
    if( m_hRC ) 
    {
        m_hDC = pCanvas->m_hDC;
        m_iPixelFormat = pCanvas->m_iPixelFormat;
        wglMakeCurrent(m_hDC,m_hRC);
        return true;
    }

    return false;
}

inline void 
CRenderDevice::_drawPrimitive( EPrimitiveType enumType, int iStartVertex, int iPrimitiveCount )
{
    int iCheck = 0;
    glGetIntegerv( GL_ARRAY_BUFFER_BINDING, &iCheck );

    if( iCheck ? 1 : 0 )
    {
        int iCount = 0;
        int iMode = 0;
        int iStep = 0;

        switch(enumType)
        {
        case E_PRIMITIVE_TYPE_TRIANGLE_LIST:
            iMode = 3;
            iStep = 0;
            break;
        case E_PRIMITIVE_TYPE_TRIANGLE_FAN:
        case E_PRIMITIVE_TYPE_TRIANGLE_STRIP:
            iMode = 3;
            iStep = 2;
            break;
        case E_PRIMITIVE_TYPE_LINES:
            iMode = 2;
            iStep = 0;
            break;
        case E_PRIMITIVE_TYPE_LINE_LOOP:
        case E_PRIMITIVE_TYPE_LINE_STRIP:
            iMode = 2;
            iStep = 1;
            break;
        case E_PRIMITIVE_TYPE_POINTS:
            iMode = 1;
            iStep = 0;
            break;
        default:
            assert(0);
        }

        for(int i=0; iStep != 0 && i<iPrimitiveCount-1; i++)
        {
            iCount += iStep;
        }

        iCount = (iPrimitiveCount * iMode) - iCount;

        glDrawArrays( enumType, iStartVertex, (unsigned int)iCount );
    }
}

inline void 
CRenderDevice::_drawIndexPrimitive( EPrimitiveType enumType, int iNumVertex, int iStartIndex, int iPrimitiveCount )
{
    int iCheckA = 0;
    int iCheckE = 0;
    glGetIntegerv( GL_ARRAY_BUFFER_BINDING, &iCheckA );
    glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &iCheckE );

    if( (iCheckA ? 1 : 0) && (iCheckE ? 1: 0) )
    {
        #define BUFFER_OFFSET(i) ((char*)NULL + (i))

        int iCount = 0;
        if( m_RenderStates.m_pCurIndexBufferObj )
        {
            switch(enumType)
            {
            case E_PRIMITIVE_TYPE_TRIANGLE_LIST:
                iCount = (iPrimitiveCount * 3);
                break;
            case E_PRIMITIVE_TYPE_TRIANGLE_STRIP:
            case E_PRIMITIVE_TYPE_TRIANGLE_FAN:
                iCount = 3 + ( iPrimitiveCount - 1 );
                break;
            case E_PRIMITIVE_TYPE_LINES:
                iCount = ( iPrimitiveCount * 2 );
                break;
            case E_PRIMITIVE_TYPE_LINE_LOOP:
            case E_PRIMITIVE_TYPE_LINE_STRIP:
                iCount = 2 + ( iPrimitiveCount - 1 );
                break;
            case E_PRIMITIVE_TYPE_POINTS:
                iCount = iPrimitiveCount;
                break;
            default:
                assert(0);
                break;
            }

            switch( m_RenderStates.m_eCurIndexBufferType )
            {
            case E_IBUFFER_TYPE_UNSIGNED_BYTE:
                iStartIndex *= sizeof(unsigned char);
                break;
            case E_IBUFFER_TYPE_UNSIGNED_SHOR:
                iStartIndex *= sizeof(unsigned short);
                break;
            case E_IBUFFER_TYPE_UNSIGNED_INT:
                iStartIndex *= sizeof(unsigned int);
                break;
            }

            glDrawElements( enumType, (unsigned int)iCount, m_RenderStates.m_eCurIndexBufferType , BUFFER_OFFSET(iStartIndex) );
        }
    }
}

inline bool 
CRenderDevice::_beginDrawing( void )
{
    #define BUFFER_OFFSET(i) ((char*)NULL + (i))

    if( m_RenderStates.m_bIsVertexDeclDirty || m_RenderStates.m_bIsVertexStrideDirty || m_RenderStates.m_bIsVertexShaderDirty )
    {
        int iSize = static_cast<int>( m_RenderStates.m_pCurVertexDeclObj->m_vectSData.size() );
        int iSizeSemantic = (m_RenderStates.m_pCurBindVertexShaderObj != NULL ? m_RenderStates.m_pCurBindVertexShaderObj->_getSemanticSize() : 
                                                                                m_RenderStates.m_pCurBindPassObj->_getSemanticSize() );

        if( iSize == 0 || iSizeSemantic == 0 ) 
            return false;

        for( int i=0; i<iSize; i++ )
        {
            for( int j=0; j<iSizeSemantic; j++ )
            {
                SSemantic* pSemantic = (m_RenderStates.m_pCurBindVertexShaderObj != NULL ? m_RenderStates.m_pCurBindVertexShaderObj->_getSemantic(j) : 
                                                                                           m_RenderStates.m_pCurBindPassObj->_getSemantic(j) );

                if( m_RenderStates.m_pCurVertexDeclObj->m_vectSData[i].m_sSemantic == pSemantic->m_sSemantic )
                {
                    cgGLEnableClientState( pSemantic->m_cgParam );
                    cgGLSetParameterPointer( pSemantic->m_cgParam, 
                                             m_RenderStates.m_pCurVertexDeclObj->m_vectSData[i].m_iStride,
                                             m_RenderStates.m_pCurVertexDeclObj->m_vectSData[i].m_iType,
                                             m_RenderStates.m_iCurVertexStride,
                                             BUFFER_OFFSET( m_RenderStates.m_pCurVertexDeclObj->m_vectSData[i].m_iOffset) );

                    pSemantic->m_bEnable = true;
                    break;
                }
            }
        }

        if( m_RenderStates.m_bIsVertexDeclDirty ) m_RenderStates.m_bIsVertexDeclDirty = false;
        if( m_RenderStates.m_bIsVertexStrideDirty ) m_RenderStates.m_bIsVertexStrideDirty = false;
        if( m_RenderStates.m_bIsVertexShaderDirty ) m_RenderStates.m_bIsVertexShaderDirty = false;
    }

    return true;
}

inline void 
CRenderDevice::_endDrawing( EPrimitiveType enumType, int iPrimitiveCount )
{
    m_RenderingInfoPerFrame.m_iNumOfBatches++;

    switch( enumType )
    {
    case E_PRIMITIVE_TYPE_TRIANGLE_LIST:
        m_RenderingInfoPerFrame.m_iNumOfTrianglePrimitives += iPrimitiveCount;
        break;
    case E_PRIMITIVE_TYPE_TRIANGLE_STRIP:
        m_RenderingInfoPerFrame.m_iNumOfTriangleStripPrimitives += iPrimitiveCount;
        break;
    case E_PRIMITIVE_TYPE_TRIANGLE_FAN:
        m_RenderingInfoPerFrame.m_iNumOfTriangleFanPrimitives += iPrimitiveCount;
        break;
    case E_PRIMITIVE_TYPE_POINTS:
        m_RenderingInfoPerFrame.m_iNumOfPointPrimitives += iPrimitiveCount;
        break;
    case E_PRIMITIVE_TYPE_LINE_STRIP:
        m_RenderingInfoPerFrame.m_iNumOfLineStripPrimitives += iPrimitiveCount;
        break;
    case E_PRIMITIVE_TYPE_LINE_LOOP:
        m_RenderingInfoPerFrame.m_iNumOfLineLoopPrimitives += iPrimitiveCount;
        break;
    case E_PRIMITIVE_TYPE_LINES:
        m_RenderingInfoPerFrame.m_iNumOfLinePrimitives += iPrimitiveCount;
        break;
    }
}

//--------------------------------------------------
// struct CRenderDevice::SRenderStatesCallbacks
//--------------------------------------------------
CRenderDevice* CRenderDevice::SRenderStatesCallbacks::pRenderDevice = NULL;

CGbool CRenderDevice::SRenderStatesCallbacks::set( CGstateassignment sa )
{
    assert( pRenderDevice );

    CGbool bResult = CG_TRUE;
    if( pRenderDevice )
        bResult = _checkingState( sa );

    return bResult;
}

CGbool CRenderDevice::SRenderStatesCallbacks::reset( CGstateassignment sa )
{
    assert( pRenderDevice );

    CGbool bResult = CG_TRUE;
    if( pRenderDevice )
        bResult = _checkingState( sa );

    return bResult;
}

CGbool CRenderDevice::SRenderStatesCallbacks::validate( CGstateassignment sa )
{
    assert( pRenderDevice );
    return CG_TRUE;
}

CGbool CRenderDevice::SRenderStatesCallbacks::_checkingState( CGstateassignment& sa )
{
    int iCount = 0;
    CGstate state = CG_ASSERT( cgGetStateAssignmentState(sa) );
    std::string sStateName = CG_ASSERT( cgGetStateName( state ) );

    if( sStateName == "ClearColor" )
    {
        const float* pVal = CG_ASSERT( cgGetFloatStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
            pRenderDevice->setColorBuffer( pVal[0], pVal[1], pVal[2], pVal[3] );
    }
    else if( sStateName == "ClearDepth" )
    {
        const float* pVal = CG_ASSERT( cgGetFloatStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
            pRenderDevice->setDepthBuffer( pVal[0] );
    }       
    else if( sStateName == "ClearStencil" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
            pRenderDevice->setStencilBuffer( pVal[0] );
    }
    else if( sStateName == "ColorMask" )
    {
        const CGbool* pVal = CG_ASSERT( cgGetBoolStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            pRenderDevice->setColorBufferMask( (pVal[0] ? true : false), 
                                               (pVal[1] ? true : false), 
                                               (pVal[2] ? true : false), 
                                               (pVal[3] ? true : false) );
        }
    }
    else if( sStateName == "DepthMask" )
    {
        const CGbool* pVal = CG_ASSERT( cgGetBoolStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
            pRenderDevice->setDepthBufferMask( (pVal[0] ? true : false) );
    }
    else if( sStateName == "StencilMask" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
            pRenderDevice->setStencilBufferMask( E_FACE_FRONTBACK, pVal[0] );
    }
    else if( sStateName == "StencilMaskSeparate" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
            pRenderDevice->setStencilBufferMask( static_cast<EFaceType>(pVal[0]), pVal[1] );
    }
    else if( sStateName == "AlphaTestEnable" )
    {
        const CGbool* pVal = CG_ASSERT( cgGetBoolStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CAlphaStateObject AlphaStateObj = pRenderDevice->getAlphaStateObject();
            AlphaStateObj.m_bEnable = (pVal[0] ? true : false);
            pRenderDevice->bindAlphaStateObject( AlphaStateObj );
        }
    }
    else if( sStateName == "AlphaFunc" )
    {
        const float* pVal = CG_ASSERT( cgGetFloatStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CAlphaStateObject AlphaStateObj = pRenderDevice->getAlphaStateObject();
            AlphaStateObj.m_eCmpFunc = static_cast<ECmpFunc>(static_cast<int>(pVal[0]));
            AlphaStateObj.m_fAlphaRef = pVal[1];
            pRenderDevice->bindAlphaStateObject( AlphaStateObj );
        }
    }
    else if( sStateName == "BlendEnable" )
    {
        const CGbool* pVal = CG_ASSERT( cgGetBoolStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CBlendStateObject BlendStateObj = pRenderDevice->getBlendStateObject();
            BlendStateObj.m_bEnable = (pVal[0] ? true : false);
            pRenderDevice->bindBlendStateObject( BlendStateObj );
        }
    }
    else if( sStateName == "BlendFunc" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CBlendStateObject BlendStateObj = pRenderDevice->getBlendStateObject();
            BlendStateObj.m_eSrcBlendFactor = static_cast<EBlendFactor>(pVal[0]);
            BlendStateObj.m_eDstBlendFactor = static_cast<EBlendFactor>(pVal[1]);
            pRenderDevice->bindBlendStateObject( BlendStateObj );
        }
    }
    else if( sStateName == "BlendEquation" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CBlendStateObject BlendStateObj = pRenderDevice->getBlendStateObject();
            BlendStateObj.m_eBlendOp = static_cast<EBlendOp>(pVal[0]);
            pRenderDevice->bindBlendStateObject( BlendStateObj );
        }
    }
    else if( sStateName == "DepthTestEnable" )
    {
        const CGbool* pVal = CG_ASSERT( cgGetBoolStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CDepthStateObject DepthStateObj = pRenderDevice->getDepthStateObject();
            DepthStateObj.m_bEnable = (pVal[0] ? true : false);
            pRenderDevice->bindDepthStateObject( DepthStateObj );
        }
    }
    else if( sStateName =="DepthFunc" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CDepthStateObject DepthStateObj = pRenderDevice->getDepthStateObject();
            DepthStateObj.m_eCmpFunc = static_cast<ECmpFunc>(pVal[0]);
            pRenderDevice->bindDepthStateObject( DepthStateObj );
        }
    }
    else if( sStateName == "StencilTestEnable" )
    {
        const CGbool* pVal = CG_ASSERT( cgGetBoolStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CStencilStateObject StencilStateObj = pRenderDevice->getStencilStateObject();
            StencilStateObj.m_bEnable = (pVal[0] ? true : false);
            pRenderDevice->bindStencilStateObject( StencilStateObj );
        }
    }
    else if( sStateName == "StencilFunc" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CStencilStateObject StencilStateObj = pRenderDevice->getStencilStateObject();
            StencilStateObj.m_eCmpFunc = static_cast<ECmpFunc>(pVal[0]);
            StencilStateObj.m_iStencilRef = pVal[1];
            StencilStateObj.m_uiStencilMask = static_cast<unsigned int>(pVal[2]);
            pRenderDevice->bindStencilStateObject( StencilStateObj );
        }
    }
    else if( sStateName == "StencilOp" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CStencilStateObject StencilStateObj = pRenderDevice->getStencilStateObject();
            StencilStateObj.m_eStencilFail = static_cast<EStencilOp>(pVal[0]);
            StencilStateObj.m_eDepthFail = static_cast<EStencilOp>(pVal[1]);
            StencilStateObj.m_eDepthStencilAllPass = static_cast<EStencilOp>(pVal[2]);
            pRenderDevice->bindStencilStateObject( StencilStateObj );
        }
    }
    else if( sStateName == "ScissorTestEnable" )
    {
        const CGbool* pVal = CG_ASSERT( cgGetBoolStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CScissorStateObject ScissorStateObj = pRenderDevice->getScissorStateObject();
            ScissorStateObj.m_bEnable = (pVal[0] ? true : false);
            pRenderDevice->bindScissorStateObject( ScissorStateObj );
        }
    }
    else if( sStateName == "Scissor" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CScissorStateObject ScissorStateObj = pRenderDevice->getScissorStateObject();
            ScissorStateObj.m_iX = pVal[0];
            ScissorStateObj.m_iY = pVal[1];
            ScissorStateObj.m_iWidth = static_cast<int>(pVal[2]);
            ScissorStateObj.m_iHeight = static_cast<int>(pVal[3]);
            pRenderDevice->bindScissorStateObject( ScissorStateObj );
        }
    }
    else if( sStateName == "CullFaceEnable" )
    {
        const CGbool* pVal = CG_ASSERT( cgGetBoolStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CCullFaceStateObject CullFaceStateObj = pRenderDevice->getCullFaceStateObject();
            CullFaceStateObj.m_bEnable = (pVal[0] ? true : false);
            pRenderDevice->bindCullFaceStateObject( CullFaceStateObj );
        }
    }
    else if( sStateName == "CullFace" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CCullFaceStateObject CullFaceStateObj = pRenderDevice->getCullFaceStateObject();
            CullFaceStateObj.m_eFaceType = static_cast<EFaceType>(pVal[0]);
            pRenderDevice->bindCullFaceStateObject( CullFaceStateObj );
        }
    }
    else if( sStateName == "FrontFace" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CCullFaceStateObject CullFaceStateObj = pRenderDevice->getCullFaceStateObject();
            CullFaceStateObj.m_eCullFace = static_cast<ECullFace>(pVal[0]);
            pRenderDevice->bindCullFaceStateObject( CullFaceStateObj );
        }
    }
    else if( sStateName == "PolygonOffsetFillEnable" )
    {
        const CGbool* pVal = CG_ASSERT( cgGetBoolStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CPolygonOffsetStateObject PolygonOffsetStateObj = pRenderDevice->getPolygonOffsetStateObject();
            PolygonOffsetStateObj.m_bEnable = (pVal[0] ? true : false);
            pRenderDevice->bindPolygonOffsetStateObject( PolygonOffsetStateObj );
        }
    }
    else if( sStateName == "PolygonOffset" )
    {
        const float* pVal = CG_ASSERT( cgGetFloatStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CPolygonOffsetStateObject PolygonOffsetStateObj = pRenderDevice->getPolygonOffsetStateObject();
            PolygonOffsetStateObj.m_fFactor = pVal[0];
            PolygonOffsetStateObj.m_fUnits = pVal[1];
            pRenderDevice->bindPolygonOffsetStateObject( PolygonOffsetStateObj );
        }
    }
    else if( sStateName == "PolygonMode" )
    {
        const int* pVal = CG_ASSERT( cgGetIntStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
        {
            CPolygonModeStateObject PolygonModeObj = pRenderDevice->getPolygonModeStateObject();
            PolygonModeObj.m_eFaceType = static_cast<EFaceType>( pVal[0] );
            PolygonModeObj.m_eMode = static_cast<EPolygonMode>( pVal[1] );
            pRenderDevice->bindPolygonModeStateObject( PolygonModeObj );
        }
    }
    else if( sStateName == "PointSize" )
    {
        const float* pVal = CG_ASSERT( cgGetFloatStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
            pRenderDevice->setPointSize( pVal[0] );
    }
    else if( sStateName == "LineWidth" )
    {
        const float* pVal = CG_ASSERT( cgGetFloatStateAssignmentValues( sa, &iCount ) );
        if( iCount > 0 )
            pRenderDevice->setLineWidth( pVal[0] );
    }

    return CG_TRUE;
}

//--------------------------------------------------
// struct CRenderDevice::SRenderStates
//--------------------------------------------------
void CRenderDevice::SRenderStates::reset( void )
{
    m_fPointSize = 1.0f;
    m_fLineWidth = 1.0f;

    //----------------------------------
    // Default value of buffer
    //----------------------------------
    for(int i=0; i<4; i++)
        m_fClearColor[i] = 0;

    m_fClearDepth = 1;
    m_iClearStencil = 0;

    //---------------------------------
    // Default mask of buffer
    //---------------------------------
    for(int i=0; i<4; i++)
        m_bColorWrite[i] = true;

    m_bDepthWrtie = true;
    m_uiStencilMask = -1;

    //---------------------------------
    // RenderState
    //---------------------------------
    m_iVSyncInterval = 1;

    m_AlphaStateObj.reset();
    m_BlendStateObj.reset();
    m_DepthStateObj.reset();
    m_StencilStateObj.reset();
    m_ScissorStateObj.reset();
    m_CullFaceStateObj.reset();
    m_PolygonOffsetStateObj.reset();
    m_PolygonModeStateObj.reset();

    m_CurViewportData.reset();

    //---------------------------------
    // Buffer setup
    //---------------------------------
    m_pCurBindVertexShaderObj = NULL;
    m_pCurBindGeometryShaderObj = NULL;
    m_pCurBindPixelShaderObj = NULL;
    m_pCurBindPassObj = NULL;
    m_bFXResotreState = true;
    m_pCurVertexDeclObj = NULL;
    m_pCurStreamBufferObj = NULL;
    m_pCurIndexBufferObj = NULL;
    m_eCurIndexBufferType = E_IBUFFER_TYPE_UNSIGNED_BYTE;
    m_iCurVertexStride = 0;
    m_uiCurFrameBufferID = 0;
    m_pWordBufferObj = NULL;
    m_pWordIndexBufferObj = NULL;

    //----------------------------------
    // Others:
    //----------------------------------
    m_bIsVertexDeclDirty = false;
    m_bIsVertexStrideDirty = false;
    m_bIsVertexShaderDirty = false;
}

}//End namespace GL

}//End namespace Porman